package network.tpa2;

import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;

import network.AbstractNetworkInterface;
import network.AbstractUserSignal;
import network.ListenerBindException;
import network.config.BadPortException;
import network.config.NetworkConfiguration;
import network.config.NetworkConfiguration.PortType;
import network.SendSignalException;
import user.LocalUser;
import controller.MessageController;
import controller.UserNotConnected;

public class NetworkInterfaceA2 extends AbstractNetworkInterface {
	
	private String createLocalUserName () {
		return LocalUser.getLocalUser().getUsername() + "@" + LocalUser.getLocalUser().getAddress().getHostAddress() ;
	}
	
	public void sendHello (String username) throws SendSignalException {
		super.sender.sendSignal(new UserSignalA2((new HelloCreator(createLocalUserName(), true)).createHello(), null)) ;
	}
	
	public void sendHelloReply (String username, InetAddress remoteAddress) throws SendSignalException {
		super.sender.sendSignal(new UserSignalA2((new HelloCreator(createLocalUserName(), false)).createHello(), remoteAddress)) ;
	}
	
	public void sendGoodBye () throws SendSignalException {
		super.sender.sendSignal(new UserSignalA2((new ByeCreator (createLocalUserName())).createBye(), null)) ;
	}
	
	public void sendAskLogin (InetAddress to) throws SendSignalException {
		super.sender.sendSignal(new UserSignalA2((new HelloCreator(createLocalUserName(), false)).createHello(), to)) ;
	}
	
	public void sendMessage (String message, InetAddress to, InetAddress[] cc) throws SendSignalException {
		super.sender.sendSignal(new UserSignalA2((new MessageCreator(createLocalUserName(), message)).createMsg(), to)) ;
	}
	
	public void sendPropFile (String filename, long fileSize, int fileID, InetAddress to) throws SendSignalException {
		this.fileSendIDs.put(createIdentifier(filename, LocalUser.getLocalUser().getAddress(), fileSize), fileID) ;
		super.sender.sendSignal(new UserSignalA2((new FileCreator(createLocalUserName(), 
					fileSize, filename, -1)).createFile(), to)) ;
	}
	
	public void sendAcceptFile(InetAddress to, int fileID, boolean accepted, boolean now) throws SendSignalException {
		long size = this.fileRecvSize.remove(fileID) ;
		String filename = this.fileRecvName.remove(fileID) ;
		super.sender.sendSignal(new UserSignalA2((new FileCreator(createLocalUserName(), 
				size, filename, super.networkConfig.getPort(PortType.TCP_ACCEPT))).createFile(), to)) ;
	}
	
	/**
	 * Create a new "unique" file identifier
	 * 
	 * @param filename The filename
	 * @param addr The sender address
	 * @param fileSize The size of the file
	 * @return
	 */
	private String createIdentifier (String filename, InetAddress addr, long fileSize) {
		return filename + addr.getHostAddress() + String.valueOf(fileSize) ;
	}
	
	/**
	 * Make a correspondance between A2 identifier and file ID
	 */
	private HashMap <String, Integer> fileSendIDs ;
	
	/**
	 * Make a correspondance between file ID and name / size
	 */
	private HashMap <Integer, String> fileRecvName ;
	private HashMap <Integer, Long> fileRecvSize ;
	
	/**
	 * Create a new NetworkInterface connected with the specified MessageController
	 * 
	 * @param msgCtrl
	 * @throws ListenerBindException if an error occurs starting the udp listener thread
	 */
	public NetworkInterfaceA2 (MessageController msgCtrl, NetworkConfiguration nc) throws ListenerBindException {
		super (msgCtrl, nc) ;
		this.fileSendIDs = new HashMap <String, Integer> () ;
		this.fileRecvSize = new HashMap <Integer, Long> () ;
		this.fileRecvName = new HashMap <Integer, String> () ;
	}
	

	@Override
	protected void addSignal(DatagramPacket packet) {
		addAbstractSignal(new UserSignalA2(packet)) ;
	}
	
	/**
	 * Perform action corresponding to the specified signal using
	 * the message controller.
	 * 
	 * @param si
	 * @throws UserNotConnected 
	 * @throws SendSignalException 
	 * @throws UnknownHostException 
	 */
	protected void performSignal (AbstractUserSignal aus) throws UnknownHostException, SendSignalException, UserNotConnected {
		UserSignalA2 usi = (UserSignalA2)aus ;
		InetAddress addr = usi.getAddress() ;
		MsgDecoder decode = new MsgDecoder(usi.getSignal()) ;
		if(decode.getTypeMess().compareToIgnoreCase(HelloCreator.typeMess) == 0) {
			if (decode.getNeedHelloBack()) {
				super.msgCtrl.processHello (decode.getAuthor().split("@")[0], addr) ;
			}
			else {
				super.msgCtrl.processHelloReply (decode.getAuthor().split("@")[0], addr) ;
			}
		}
		else if(decode.getTypeMess().compareToIgnoreCase(MessageCreator.typeMess) == 0) {
		
			super.msgCtrl.processSendText(decode.getMessage(), new InetAddress[] { LocalUser.getLocalUser().getAddress()  }, addr) ;
		}
		else if(decode.getTypeMess().compareToIgnoreCase(FileCreator.typeMess) == 0) {
			if (decode.getFlagAccepted() < 0) {
				/* Reception d'une demande. */
				int ID = nextID () ;
				this.fileRecvName.put(ID, decode.getFileName()) ;
				this.fileRecvSize.put(ID, decode.getFileSize()) ;
				super.msgCtrl.processPropFile(decode.getFileName(), ID, decode.getFileSize(), addr) ;
			}
			else if (decode.getFlagAccepted() < 1024) {
				/* Refus. */
				super.msgCtrl.processAcceptFile(this.fileSendIDs.get(createIdentifier(decode.getFileName(), LocalUser.getLocalUser().getAddress(), decode.getFileSize())), 
						false, false, addr) ;
			}
			else {
				/* Acceptation. */
				try {
					super.networkConfig.setPort(super.networkConfig.getCurrentProtocol(), PortType.TCP_CONNECT, decode.getFlagAccepted()) ;
					String identifier = createIdentifier(decode.getFileName(), LocalUser.getLocalUser().getAddress(), decode.getFileSize()) ;
					if (this.fileSendIDs.containsKey(identifier)) {
						super.msgCtrl.processAcceptFile(this.fileSendIDs.get(identifier), true, true, addr) ;
					}
				} 
				catch (BadPortException e) {
					e.printStackTrace();
				}
			}
		}
		else if (decode.getTypeMess().compareToIgnoreCase(ByeCreator.typeMess) == 0) {
			super.msgCtrl.processGoodBye(addr) ;
		}
	}
	
	private static int nextID () {
		return (int)System.currentTimeMillis() ;
	}
	
}
